package org.springframework.data.mybatis.repository.support;

import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

/**
 * mybatis仓库实现类
 * 
 * @author martin_duan
 *
 * @param <T>
 * @param <ID>
 */
@Repository
@Transactional(readOnly = true)
public class SimpleMybatisRepository<T, ID extends Serializable> extends SqlSessionRepositorySupport implements MybatisRepository<T, ID> {

	private final MybatisEntityInformation<T, ID> entityInformation;

	@SuppressWarnings("unused")
	private AuditorAware<Long> auditorAware;

	public SimpleMybatisRepository(MybatisEntityInformation<T, ID> entityInformation, SqlSessionTemplate sqlSessionTemplate) {
		super(sqlSessionTemplate);
		this.entityInformation = entityInformation;
	}

	@Override
	protected String getNamespace() {
		return entityInformation.getJavaType().getName();
	}

	/**
	 * 插入
	 */
	@Override
	@Transactional
	public <S extends T> S insert(S entity) {
		entityInformation.setCreatedDate(entity);
		entityInformation.setCreatedBy(entity);
		if (entityInformation.hasVersion()) {
			entityInformation.setVersion(entity, 0);
		}
		insert("_insert", entity);
		return entity;
	}

	/**
	 * 根据ID更新
	 */
	@Override
	@Transactional
	public <S extends T> S update(S entity) {
		entityInformation.setLastModifiedDate(entity);
		entityInformation.setLastModifiedBy(entity);
		update("_update", entity);
		if (entityInformation.hasVersion()) {
			entityInformation.increaseVersion(entity);
		}
		return entity;
	}

	/**
	 * 根据ID更新,并忽略空参数
	 */
	@Override
	@Transactional
	public <S extends T> S updateIgnoreNull(S entity) {
		entityInformation.setLastModifiedDate(entity);
		entityInformation.setLastModifiedBy(entity);
		update("_updateIgnoreNull", entity);
		if (entityInformation.hasVersion()) {
			entityInformation.increaseVersion(entity);
		}
		return entity;
	}

	/**
	 * 保存或者更新
	 */
	@Override
	@Transactional
	public <S extends T> S save(S entity) {
		Assert.notNull(entity, "entity can not be null");
		if (entityInformation.isNew(entity)) {
			insert(entity);
		} else {
			update(entity);
		}
		return entity;
	}

	/**
	 * 保存或者更新,并忽略空参数
	 */
	@Override
	@Transactional
	public <S extends T> S saveIgnoreNull(S entity) {
		Assert.notNull(entity, "entity can not be null");
		if (entityInformation.isNew(entity)) {
			insert(entity);
		} else {
			updateIgnoreNull(entity);
		}
		return entity;
	}

	/**
	 * 批量保存
	 */
	@Override
	@Transactional
	public <S extends T> List<S> save(Iterable<S> entities) {
		if (null == entities) {
			return Collections.emptyList();
		}
		for (S entity : entities) {
			save(entity);
		}
		return (List<S>) entities;
	}

	private <X extends T, S extends T> int updateByCondition(X condition, S update, boolean isEqCondition) {
		if (update == null) {
			return 0;
		}
		entityInformation.setLastModifiedDate(update);
		entityInformation.setLastModifiedBy(update);
		Map<String, Object> params = new HashMap<String, Object>();
		params.put(isEqCondition ? "_eq_condition" : "_condition", condition);
		params.put("_update", update);
		int row = update("_updateByCondition", params);
		if (entityInformation.hasVersion()) {
			entityInformation.increaseVersion(update);
		}
		return row;
	}

	/**
	 * 根据注解条件更新
	 */
	@Override
	@Transactional
	public <X extends T, S extends T> int updateByCondition(X condition, S update) {
		return updateByCondition(condition, update, false);
	}

	/**
	 * 根据全等条件更新
	 */
	@Override
	@Transactional
	public <X extends T, S extends T> int updateByEqCondition(X condition, S update) {
		return updateByCondition(condition, update, true);
	}

	private <X extends T> int deleteByCondition(X condition, boolean isEqCondition) {
		Map<String, Object> params = new HashMap<String, Object>();
		if (condition != null) {
			params.put(isEqCondition ? "_eq_condition" : "_condition", condition);
		}
		return super.delete("_deleteByCondition", params);
	}

	/**
	 * 根据注解条件删除
	 */
	@Override
	@Transactional
	public <X extends T> int deleteByCondition(X condition) {
		return deleteByCondition(condition, false);
	}

	/**
	 * 根据全等条件删除
	 */
	@Override
	@Transactional
	public <X extends T> int deleteByEqCondition(X condition) {
		return deleteByCondition(condition, true);
	}

	/**
	 * 根据注解条件批量删除
	 */
	@Override
	@Transactional
	public void deleteByCondition(Iterable<T> entities) {
		if (null == entities) {
			return;
		}
		for (T entity : entities) {
			deleteByCondition(entity);
		}
	}

	/**
	 * 根据全等条件批量删除
	 */
	@Override
	@Transactional
	public void deleteByEqCondition(Iterable<T> entities) {
		if (null == entities) {
			return;
		}
		for (T entity : entities) {
			deleteByEqCondition(entity);
		}
	}

	/**
	 * 根据ID删除
	 */
	@SuppressWarnings("deprecation")
	@Override
	@Transactional
	public void delete(ID id) {
		Assert.notNull(id);
		delete("_deleteById", id);
	}

	/**
	 * 根据实体ID删除
	 */
	@SuppressWarnings("deprecation")
	@Override
	@Transactional
	public void delete(T entity) {
		Assert.notNull(entity);
		delete(entityInformation.getId(entity));
	}

	/**
	 * 根据实体ID批量删除
	 */
	@Override
	@Transactional
	public void delete(Iterable<? extends T> entities) {
		if (null == entities) {
			return;
		}
		for (T entity : entities) {
			delete(entity);
		}
	}

	/**
	 * 删除所有
	 */
	@Override
	@Transactional
	public void deleteAll() {
		delete("_deleteAll");
	}

	/**
	 * 根据ID查找
	 */
	@Override
	public T findOne(ID id) {
		Assert.notNull(id, "id can not be null");
		return selectOne("_findById", id);
	}
	
	/**
	 * 根据ID和版本查找
	 */
	@Override
	public T findOne(ID id, Long version) {
		Assert.notNull(id, "id can not be null");
		Assert.notNull(version, "version can not be null");
		Map<String, Object> param = new HashMap<String, Object>();
		param.put("id", id);
		param.put("version", version);
		return selectOne("_findByIdAndVersion", param);
	}

	private <X extends T> T findOneByCondition(X condition, boolean isEqCondition) {
		Map<String, Object> params = new HashMap<String, Object>();
		if (condition != null) {
			params.put(isEqCondition ? "_eq_condition" : "_condition", condition);
		}
		return selectOne("_findAll", params);
	}

	/**
	 * 根据注解条件查找
	 */
	@Override
	public <X extends T> T findOneByCondition(X condition) {
		return findOneByCondition(condition, false);
	}

	/**
	 * 根据全等条件查找
	 */
	@Override
	public <X extends T> T findOneByEqCondition(X condition) {
		return findOneByCondition(condition, true);
	}

	/**
	 * 根据ID判断是否存在
	 */
	@Override
	public boolean exists(ID id) {
		return null != findOne(id);
	}

	/**
	 * 计数
	 */
	@Override
	public long count() {
		return selectOne("_count");
	}

	private <X extends T> Long countByCondition(X condition, boolean isEqCondition) {
		Map<String, Object> params = new HashMap<String, Object>();
		if (condition != null) {
			params.put(isEqCondition ? "_eq_condition" : "_condition", condition);
		}
		return selectOne("_countByCondition", params);
	}

	/**
	 * 根据注解条件计数
	 */
	@Override
	public <X extends T> Long countByCondition(X condition) {
		return countByCondition(condition, false);
	}

	/**
	 * 根据全等条件计数
	 */
	@Override
	public <X extends T> Long countByEqCondition(X condition) {
		return countByCondition(condition, true);
	}

	/**
	 * 查找
	 */
	@Override
	public List<T> findAll() {
		return selectList("_findAll");
	}

	/**
	 * 查找
	 */
	@Override
	public List<T> findAll(Sort sort) {
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("_sorts", sort);
		return selectList("_findAll", params);
	}

	/**
	 * 查找
	 */
	@Override
	public List<T> findAll(Iterable<ID> ids) {
		if (null == ids) {
			return Collections.emptyList();
		}
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("_ids", ids);
		return selectList("_findAll", params);
	}

	/**
	 * 分页查找
	 */
	@Override
	public Page<T> findAll(Pageable pageable) {
		if (null == pageable) {
			return new PageImpl<T>(findAll());
		}
		return findByPager(pageable, "_findByPager", "_countByCondition");
	}

	private <X extends T> List<T> findAll(X condition, boolean isEqCondition) {
		Map<String, Object> params = new HashMap<String, Object>();
		if (condition != null) {
			params.put(isEqCondition ? "_eq_condition" : "_condition", condition);
		}
		return selectList("_findAll", params);
	}

	/**
	 * 根据注解条件查找
	 */
	@Override
	public <X extends T> List<T> findAllByCondition(X condition) {
		return findAll(condition, false);
	}

	/**
	 * 根据全等条件查找
	 */
	@Override
	public <X extends T> List<T> findAllByEqCondition(X condition) {
		return findAll(condition, true);
	}

	private <X extends T> Page<T> findAll(Pageable pageable, X condition, boolean isEqCondition) {
		return findByPager(pageable, "_findByPager", "_countByCondition", condition, isEqCondition);
	}

	/**
	 * 根据注解条件查找
	 */
	@Override
	public <X extends T> Page<T> findAllByCondition(Pageable pageable, X condition) {
		return findAll(pageable, condition, false);
	}

	/**
	 * 根据全等条件查找
	 */
	@Override
	public <X extends T> Page<T> findAllByEqCondition(Pageable pageable, X condition) {
		return findAll(pageable, condition, true);
	}

	private <X extends T> List<T> findAllByCondition(Sort sort, X condition, boolean isEqCondition) {
		Map<String, Object> params = new HashMap<String, Object>();
		if (condition != null) {
			params.put(isEqCondition ? "_eq_condition" : "_condition", condition);
		}
		params.put("_sorts", sort);
		return selectList("_findAll", params);
	}

	@Override
	public <X extends T> List<T> findAllByCondition(Sort sort, X condition) {
		return findAllByCondition(sort, condition, false);
	}

	@Override
	public <X extends T> List<T> findAllByEqCondition(Sort sort, X condition) {
		return findAllByCondition(sort, condition, true);
	}

	@Override
	public <S extends T> List<S> findAll(Example<S> example) {
		throw new UnsupportedOperationException();
	}

	@Override
	public <S extends T> List<S> findAll(Example<S> example, Sort sort) {
		throw new UnsupportedOperationException();
	}

	@Override
	public <S extends T> S findOne(Example<S> example) {
		throw new UnsupportedOperationException();
	}

	@Override
	public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) {
		throw new UnsupportedOperationException();
	}

	@Override
	public <S extends T> long count(Example<S> example) {
		throw new UnsupportedOperationException();
	}

	@Override
	public <S extends T> boolean exists(Example<S> example) {
		throw new UnsupportedOperationException();
	}

}
